Skip to content

fix: scope machine-only settings to application (#1032)#1034

Open
EhabY wants to merge 1 commit into
mainfrom
fix/binary-destination-application-scope
Open

fix: scope machine-only settings to application (#1032)#1034
EhabY wants to merge 1 commit into
mainfrom
fix/binary-destination-application-scope

Conversation

@EhabY

@EhabY EhabY commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Changes these settings from "scope": "machine" to "scope": "application" (all keep ignoreSync: true, so Settings Sync behavior is unchanged):

  • coder.binarySource
  • coder.binaryDestination
  • coder.headerCommand
  • coder.tlsCertFile
  • coder.tlsKeyFile
  • coder.tlsCaFile
  • coder.tlsAltHost
  • coder.tlsCertRefreshCommand
  • coder.proxyLogDirectory
  • coder.proxyBypass (deprecated in favor of http.noProxy)
  • coder.sshConfig
  • coder.sshFlags
  • coder.globalFlags

Only the local-vs-remote read scope changes; sync behavior stays exactly as before. (We considered opting a few non-path settings into sync now that they are application-scoped, but held off: a download URL, a TLS hostname, a no_proxy list, or CLI flags can still be machine- or network-specific, and that is a separate decision from this fix.)

Why

machine scope was introduced in #965 (SEC-200) to stop workspace and folder settings.json from overriding these settings. application scope preserves that goal: it can only be set in User settings, never overridden by workspace or folder settings.

But machine scope also makes VS Code stop honoring the User-configured value in a remote window. Reporters in #1032 saw the first connection work and later connections fail with no settings change; two independent users confirmed an application-scope build fixes it on Windows 11 and pinned the regression to v1.14.6, the release that shipped #965. All these settings are read at the same pre-connection point (right before SSH connects), so they share the exposure. Array settings (sshFlags/globalFlags) behave like the string ones, since scope resolution is per-key regardless of value type.

The VS Code mechanism

In a remote window, WorkspaceService filters the local user configuration through LOCAL_MACHINE_SCOPES = [APPLICATION, WINDOW, RESOURCE, LANGUAGE_OVERRIDABLE] (getLocalUserConfigurationScopes in configurationService.ts), which excludes MACHINE (see configuration.ts). A machine-scoped value can then only come from the remote machine's settings file, not the local one where the user set it. This is deliberate, long-standing design:

Reproduction (the "sometimes")

That unit test drops the value on a single connect because the schema is already registered when WorkspaceService is built. At runtime the extension registers its schema during activation, after the window's first parse, so the outcome is timing-dependent. The deciding logic is shouldInclude (configurationModels.ts): an unregistered key (scope undefined) is kept, while a machine-registered key is dropped under LOCAL_MACHINE_SCOPES.

  1. The remote window opens and parses settings.json before the extension activates, so the key is unregistered and kept.
  2. The extension activates (onResolveRemoteAuthority) and registers its machine-scoped schema.
  3. The next re-parse of settings.json (an edit, a Settings Sync write, a profile switch, or a tasks.json load) re-applies the filter, now sees MACHINE, and drops the key to its default. There is no remote fallback.

Reproduced with a source build of VS Code and a minimal UI extension (scopetest.machineSetting, scope: machine) on an SSH remote, with local settings "scopetest.machineSetting": "value-from-USER-settings" and no remote fallback. Editing settings.json after activation flips the resolved value:

moment schema registered resolved scope getValue('scopetest.machineSetting')
initial parse no (unregistered) "value-from-USER-settings"
after edit / re-parse yes MACHINE (2) "" (default)

shouldInclude logged the deciding step at each parse (scopes is LOCAL_MACHINE_SCOPES = [1,4,5,6], no MACHINE):

shouldInclude key=scopetest.machineSetting propertySchema.scope=undefined options.scopes=[1,4,5,6]  -> included
shouldInclude key=scopetest.machineSetting propertySchema.scope=2         options.scopes=[1,4,5,6]  -> excluded

APPLICATION (1) is in LOCAL_MACHINE_SCOPES, so an application-scoped key is honored regardless of timing. That is the fix.

This PR and investigation were assisted by Coder Agents on behalf of @EhabY.

@EhabY EhabY self-assigned this Jul 15, 2026
EhabY added a commit that referenced this pull request Jul 15, 2026
… settings

- Flip coder.sshConfig and coder.proxyBypass from machine to application
  scope for the same reason as the other settings in this branch: both are
  read during pre-connection setup and are subject to the same VS Code
  local-configuration exclusion described in #1032/#1034.
- Re-review ignoreSync per setting instead of blanket-applying it. Settings
  holding a local file path or an executed local command keep
  ignoreSync: true (coder.binaryDestination, coder.headerCommand,
  coder.tlsCertFile, coder.tlsKeyFile, coder.tlsCaFile,
  coder.tlsCertRefreshCommand, coder.proxyLogDirectory, coder.globalFlags,
  coder.sshConfig). Plain URLs/hostnames/flags with no local-path dependency
  now sync like other application-scoped settings (coder.binarySource,
  coder.tlsAltHost, coder.proxyBypass, coder.sshFlags).
- Document the Settings Sync behavior in the README and CHANGELOG.
@EhabY EhabY changed the title fix: scope coder.binarySource/binaryDestination to application (test build for #1032) fix: scope machine-only settings to application (#1032) Jul 15, 2026
EhabY added a commit that referenced this pull request Jul 15, 2026
Once a window has a remote connection, VS Code stops reading
machine-scoped values from the local settings.json at all, with or
without Profiles (hasRemote is the trigger, not profile usage). Add a
README warning pointing back to #1032/#1034 so no one reintroduces
machine scope without addressing this first.
@EhabY EhabY marked this pull request as ready for review July 16, 2026 10:01
EhabY added a commit that referenced this pull request Jul 16, 2026
We reproduced the intermittent revert deterministically: in a remote
window the local User settings.json is parsed without machine scope, so
a machine-scoped value is kept only until the extension registers its
schema, after which the next re-parse (edit, Settings Sync write, profile
switch, or tasks.json load) drops it to the default with no remote-side
fallback.

Move the mechanism detail out of the changelog into the README note and
PR #1034, and remove the earlier "could not reproduce" hedge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@EhabY EhabY requested a review from code-asher July 16, 2026 13:26
EhabY added a commit that referenced this pull request Jul 16, 2026
Reword the README Settings Sync note and machine-scope warning to read
more clearly, and remove the trailing "See the README note and #1034"
sentence from the changelog entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EhabY added a commit that referenced this pull request Jul 16, 2026
We reproduced the intermittent revert deterministically: in a remote
window the local User settings.json is parsed without machine scope, so
a machine-scoped value is kept only until the extension registers its
schema, after which the next re-parse (edit, Settings Sync write, profile
switch, or tasks.json load) drops it to the default with no remote-side
fallback.

Move the mechanism detail out of the changelog into the README note and
PR #1034, and remove the earlier "could not reproduce" hedge.
@EhabY EhabY force-pushed the fix/binary-destination-application-scope branch from 4cb6849 to 77f4b97 Compare July 16, 2026 13:33
EhabY added a commit that referenced this pull request Jul 16, 2026
Reword the README Settings Sync note and machine-scope warning to read
more clearly, and remove the trailing "See the README note and #1034"
sentence from the changelog entry.
Change coder.binarySource, coder.binaryDestination, coder.headerCommand,
coder.tlsCertFile, coder.tlsKeyFile, coder.tlsCaFile, coder.tlsAltHost,
coder.tlsCertRefreshCommand, coder.proxyLogDirectory, coder.proxyBypass,
coder.sshConfig, coder.sshFlags, and coder.globalFlags from machine to
application scope (all keep ignoreSync, so Settings Sync is unchanged).

application scope keeps the SEC-200 goal of blocking workspace/folder
overrides while fixing #1032: in a remote window VS Code filters
machine-scoped keys out of the local User settings.json, so such a value
holds only until the extension registers its schema on activation. The
next re-parse of settings.json (edit, Settings Sync write, profile
switch, or tasks.json load) then drops it to the default, with no
remote-side fallback. application scope is honored regardless of timing.

Document the mechanism and reproduction in the README and PR #1034.
@EhabY EhabY force-pushed the fix/binary-destination-application-scope branch from 77f4b97 to 5734041 Compare July 16, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant